package cc.magicjson.plugin;

import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.stereotype.Component;

import java.sql.Statement;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 列名转换拦截器
 * 用于拦截MyBatis的结果集处理，对列名进行转换
 */
@Getter
@Setter
@Intercepts({
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
@ConfigurationProperties(prefix = "spring.mybatis.interceptor.column-mapping")
public class ColumnNameConversionInterceptor implements Interceptor {

    /** 是否启用拦截器 */
    private boolean enabled = false;

    /** 转换模式 */
    private ConversionMode conversionMode = ConversionMode.NONE;

    /** 是否自动忽略驼峰命名 */
    private boolean autoIgnoreCamelCase = true;

    /** 忽略的列名列表 */
    private Set<String> ignoredColumns = new HashSet<>();

    /** 转换函数 */
    private Function<String, String> converter = Function.identity();

    /** 忽略条件 */
    private Predicate<String> ignoreCondition = s -> false;

    /**
     * 设置转换模式
     *
     * @param conversionMode 转换模式
     */
    public void setConversionMode(ConversionMode conversionMode) {
        this.conversionMode = conversionMode;
        this.converter = getConverterForMode(conversionMode);
    }

    /**
     * 设置忽略的列名
     *
     * @param ignoredColumns 忽略的列名列表
     */
    public void setIgnoredColumns(List<String> ignoredColumns) {
        this.ignoredColumns = new HashSet<>(ignoredColumns);
        updateIgnoreCondition();
    }

    /**
     * 设置是否自动忽略驼峰命名
     *
     * @param autoIgnoreCamelCase 是否自动忽略驼峰命名
     */
    public void setAutoIgnoreCamelCase(boolean autoIgnoreCamelCase) {
        this.autoIgnoreCamelCase = autoIgnoreCamelCase;
        updateIgnoreCondition();
    }

    /**
     * 更新忽略条件
     */
    private void updateIgnoreCondition() {
        Predicate<String> inIgnoredColumns = ignoredColumns::contains;
        Predicate<String> isCamelCase = this::isCamelCase;
        this.ignoreCondition = inIgnoredColumns.or(
            autoIgnoreCamelCase ? isCamelCase : s -> false
        );
    }

    /**
     * 拦截方法，处理结果集
     *
     * @param invocation 调用对象
     * @return 处理后的结果
     * @throws Throwable 可能抛出的异常
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return enabled ? processResult(invocation.proceed()) : invocation.proceed();
    }

    /**
     * 处理结果对象
     *
     * @param result 原始结果对象
     * @return 处理后的结果对象
     */
    private Object processResult(Object result) {
        if (result instanceof List<?>) {
            return ((List<?>) result).stream()
                .map(this::processObject)
                .collect(Collectors.toList());
        }
        return processObject(result);
    }

    /**
     * 处理单个对象
     *
     * @param obj 待处理的对象
     * @return 处理后的对象
     */
    private Object processObject(Object obj) {
        return obj instanceof Map ? processMap((Map<?, ?>) obj) : obj;
    }

    /**
     * 处理Map对象，转换key
     *
     * @param map 原始Map
     * @return 处理后的Map
     */
    private Map<String, Object> processMap(Map<?, ?> map) {
        return map.entrySet().stream()
            .collect(Collectors.toMap(
                entry -> transformKey(entry.getKey().toString()),
                Map.Entry::getValue,
                (v1, v2) -> v2,
                LinkedHashMap::new
            ));
    }

    /**
     * 转换key
     *
     * @param key 原始key
     * @return 转换后的key
     */
    private String transformKey(String key) {
        String[] parts = key.split("\\.");
        String lastPart = parts[parts.length - 1];
        
        // 检查是否存在别名（使用 AS 关键字）
        if (lastPart.toLowerCase().contains(" as ")) {
            String[] aliasParts = lastPart.split("(?i) as ");
            String alias = aliasParts[1].trim();
            // 如果别名已经是期望的格式，直接返回
            if (!ignoreCondition.test(alias)) {
                return key;
            }
            // 否则，转换原始列名
            lastPart = aliasParts[0].trim();
        }
        
        String transformedLastPart = ignoreCondition.test(lastPart) ? lastPart : converter.apply(lastPart);
        parts[parts.length - 1] = transformedLastPart;
        return String.join(".", parts);
    }

    /**
     * 判断字符串是否为驼峰命名
     *
     * @param s 待判断的字符串
     * @return 是否为驼峰命名
     */
    private boolean isCamelCase(String s) {
        return s.matches("^[a-z]+([A-Z][a-z0-9]+)*$");
    }

    /**
     * 根据转换模式获取对应的转换函数
     *
     * @param mode 转换模式
     * @return 转换函数
     */
    private Function<String, String> getConverterForMode(ConversionMode mode) {
        switch (mode) {
            case UPPER_CASE: return String::toUpperCase;
            case LOWER_CASE: return String::toLowerCase;
            case CAMEL_CASE: return this::toCamelCase;
            default: return Function.identity();
        }
    }

    /**
     * 转换为驼峰命名
     *
     * @param input 输入字符串
     * @return 驼峰命名的字符串
     */
    private String toCamelCase(String input) {
        StringBuilder sb = new StringBuilder();
        boolean nextUpperCase = false;
        for (char c : input.toCharArray()) {
            if (c == '_') {
                nextUpperCase = true;
            } else {
                sb.append(nextUpperCase ? Character.toUpperCase(c) : Character.toLowerCase(c));
                nextUpperCase = false;
            }
        }
        return sb.toString();
    }

    /**
     * 插件方法
     *
     * @param target 目标对象
     * @return 包装后的对象
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 转换模式枚举
     */
    public enum ConversionMode {
        /** 不进行转换 */
        NONE,
        /** 转换为大写 */
        UPPER_CASE,
        /** 转换为小写 */
        LOWER_CASE,
        /** 转换为驼峰命名 */
        CAMEL_CASE
    }
}
